use std::{error::Error, fmt, fs, io, path::PathBuf};
use chrono::Local;
use serde::{Deserialize, Serialize};
use walkdir::{DirEntry, WalkDir};

pub struct FsOperator;

/// 行搜索信息
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LineInfo {
    pub path_str: String,
    pub line: String,
    pub line_num: usize,
}

impl LineInfo {
    pub fn new(path_str: String, line: String, line_num: usize) -> Self {
        LineInfo {
            path_str,
            line,
            line_num,
        }
    }
}

impl fmt::Display for LineInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let l = "-".repeat(60);
        write!(
            f,
            "{} \n文件路径: {}\n匹配内容: {}\n匹配行数: {}\n{}\n",
            &l, self.path_str, self.line, self.line_num, &l
        )
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum HandleType {
    REMOVE,
    RENAME,
}

/// 文件处理结果
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HanleRes {
    pub date: String,
    pub handle_type: HandleType,
    pub state: bool,
    pub new_path: String,
    pub old_path: String,
    pub message: String,
}

impl HanleRes {
    pub fn new(
        handle_type: HandleType,
        state: bool,
        new_path: String,
        old_path: String,
        message: String,
    ) -> Self {
        // let date = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
        let date = Local::now().format("%H:%M:%S").to_string();

        HanleRes {
            date,
            handle_type,
            state,
            new_path,
            old_path,
            message,
        }
    }
}

/// 闭包参数
pub type FnArgs = Result<DirEntry, walkdir::Error>;
/// 返回值类型
type ResType<T = Vec<PathBuf>> = Result<T, Box<dyn Error>>;

impl FsOperator {
    /// 封装 WalkDir
    pub fn walk_dir_read<T>(root_path: &str, max_depth: usize, filter_fn: T) -> ResType
    where
        T: Fn(FnArgs) -> Option<FnArgs>,
    {
        let mut result = Vec::new();

        let entrys = WalkDir::new(root_path)
            .max_depth(max_depth)
            .into_iter()
            .filter_map(filter_fn);

        for entry in entrys {
            // 替换开头的 \\?\
            let s = entry
                .unwrap()
                .path()
                .canonicalize()
                .unwrap()
                .to_str()
                .unwrap()
                .to_string();

            let s = s.replacen("\\\\?\\", "", 1);
            result.push(PathBuf::from(s));
        }

        Ok(result)
    }

    /// 读取所有文件
    pub fn read_all_deep(root_path: &str) -> ResType {
        let res = Self::walk_dir_read(root_path, usize::MAX, |e| Some(e));
        Ok(res?)
    }

    /// 读取所有的目录文件
    pub fn read_dir_deep(root_path: &str) -> ResType {
        let res = Self::walk_dir_read(root_path, usize::MAX, |e| {
            if e.as_ref().unwrap().path().is_file() {
                None
            } else {
                Some(e)
            }
        });
        Ok(res?)
    }

    /// 读取目录下的目录文件
    pub fn read_dir(root_path: &str) -> ResType {
        let res = Self::walk_dir_read(root_path, 1, |e| {
            if e.as_ref().unwrap().path().is_file() {
                None
            } else {
                Some(e)
            }
        });
        Ok(res?)
    }

    /// 读取所有的文件
    pub fn read_file_deep(root_path: &str) -> ResType {
        let res = Self::walk_dir_read(root_path, usize::MAX, |e| {
            if e.as_ref().unwrap().path().is_dir() {
                None
            } else {
                Some(e)
            }
        });
        Ok(res?)
    }

    /// 读取目录下文件
    pub fn read_file(root_path: &str) -> ResType {
        let res = Self::walk_dir_read(root_path, 1, |e| {
            if e.as_ref().unwrap().path().is_dir() {
                None
            } else {
                Some(e)
            }
        });
        Ok(res?)
    }

    /// 类似 linux grep 命令, is_deep 是否递归读取子目录, is_flog 区分大小写
    pub fn read_string_line(
        target_path: &str,
        str: &str,
        is_deep: bool,
        is_flog: bool,
    ) -> ResType<Vec<LineInfo>> {
        let mut result = Vec::new();

        let path_list;

        if is_deep {
            path_list = Self::read_file_deep(target_path)?;
        } else {
            path_list = Self::read_file(target_path)?;
        }

        for path in &path_list {
            let file_string = fs::read_to_string(path)?;
            // 行迭代
            for (line_num, line) in file_string.lines().enumerate() {
                let mut handle = || {
                    // 转换为字符串, 替换开头的 \\?\
                    let path_str = String::from(path.to_str().unwrap()).replacen("\\\\?\\", "", 1);
                    // 保存到集合中
                    result.push(LineInfo::new(path_str, String::from(line), line_num + 1));
                };

                // 是否区分大小写匹配
                if is_flog {
                    if line.to_lowercase().contains(&str.to_lowercase()) {
                        handle();
                    }
                } else {
                    if line.contains(str) {
                        handle();
                    }
                }
            }
        }

        Ok(result)
    }

    /// 删除文件或目录列表
    pub fn rm_paths(path_list: Vec<String>) -> Vec<HanleRes> {
        let mut result = Vec::new();

        // 统一处理错误
        let mut handle = |path: PathBuf, io_res: Result<(), io::Error>| {
            let path_str = path.to_str().unwrap().to_string();
            match io_res {
                Ok(_) => {
                    let msg = if path.is_file() {
                        "文件删除成功"
                    } else if path.is_dir() {
                        "目录删除成功"
                    } else {
                        "success"
                    };
                    result.push(HanleRes::new(
                        HandleType::REMOVE,
                        true,
                        "".into(),
                        path_str,
                        msg.to_string(),
                    ));
                }
                Err(err) => result.push(HanleRes::new(
                    HandleType::REMOVE,
                    false,
                    "".into(),
                    path_str,
                    err.to_string(),
                )),
            };
        };

        for item in path_list.iter() {
            let path = PathBuf::from(&item);
            if path.is_file() {
                let io_res = fs::remove_file(&path);
                handle(path, io_res);
            } else if path.is_dir() {
                let io_res = fs::remove_dir_all(&path);
                handle(path, io_res);
            }
        }
        result
    }

    // 重命名文件或目录列表
    pub fn remane_paths(items: Vec<(String, String)>) -> Vec<HanleRes> {
        let mut result = Vec::new();

        for (old_path, new_path) in items {
            let path = PathBuf::from(&old_path);

            match fs::rename(&old_path, &new_path) {
                Ok(_) => {
                    let msg = if path.is_file() {
                        "文件重命名成功"
                    } else if path.is_dir() {
                        "目录重命名成功"
                    } else {
                        "success"
                    };
                    result.push(HanleRes::new(
                        HandleType::RENAME,
                        true,
                        new_path,
                        old_path,
                        msg.to_string(),
                    ));
                }
                Err(err) => result.push(HanleRes::new(
                    HandleType::RENAME,
                    false,
                    new_path,
                    old_path,
                    err.to_string(),
                )),
            }
        }

        result
    }
}
